{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "intro_to_sparse_data_and_embeddings.ipynb",
      "version": "0.3.2",
      "views": {},
      "default_view": {},
      "provenance": [],
      "collapsed_sections": [
        "mNCLhxsXyOIS",
        "eQS5KQzBybTY",
        "copyright-notice"
      ]
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "source": [
        "#### Copyright 2017 Google LLC."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "copyright-notice"
      }
    },
    {
      "source": [
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": [],
      "metadata": {
        "colab_type": "code",
        "id": "copyright-notice2",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        },
        "cellView": "both"
      }
    },
    {
      "metadata": {
        "id": "PTaAdgy3LS8W",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " # Introduction aux donn\u00e9es rares et aux repr\u00e9sentations vectorielles continues\n",
        "\n",
        "**Objectifs d'apprentissage\u00a0:**\n",
        "* Convertir des donn\u00e9es de cha\u00eene de critique de film en un vecteur de caract\u00e9ristique clairsem\u00e9e\n",
        "* Mettre en \u0153uvre un mod\u00e8le lin\u00e9aire d'analyse des sentiments \u00e0 l'aide d'un vecteur de caract\u00e9ristique clairsem\u00e9e\n",
        "* Mettre en \u0153uvre un mod\u00e8le DNN d'analyse des sentiments \u00e0 l'aide d'une repr\u00e9sentation vectorielle continue qui projette des donn\u00e9es dans deux dimensions\n",
        "* Visualiser des repr\u00e9sentations vectorielles continues pour d\u00e9terminer ce que le mod\u00e8le a appris sur les relations entre les mots\n",
        "\n",
        "Dans cet exercice, nous allons examiner des donn\u00e9es rares et travailler avec les repr\u00e9sentations vectorielles continues \u00e0 l'aide de donn\u00e9es textuelles issues de critiques de films (provenant de l'[ensemble de donn\u00e9es IMDB ACL\u00a02011](http://ai.stanford.edu/~amaas/data/sentiment/)). Ces donn\u00e9es ont d\u00e9j\u00e0 \u00e9t\u00e9 trait\u00e9es au format `tf.Example`."
      ]
    },
    {
      "metadata": {
        "id": "2AKGtmwNosU8",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Configuration\n",
        "\n",
        "Nous allons importer nos d\u00e9pendances, et t\u00e9l\u00e9charger les donn\u00e9es de test et d'apprentissage. [`tf.keras`](https://www.tensorflow.org/api_docs/python/tf/keras) comprend un outil de mise en cache et de t\u00e9l\u00e9chargement de fichiers que nous pouvons utiliser pour r\u00e9cup\u00e9rer les ensembles de donn\u00e9es."
      ]
    },
    {
      "metadata": {
        "id": "jGWqDqFFL_NZ",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "from __future__ import print_function\n",
        "\n",
        "import collections\n",
        "import io\n",
        "import math\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import tensorflow as tf\n",
        "from IPython import display\n",
        "from sklearn import metrics\n",
        "\n",
        "tf.logging.set_verbosity(tf.logging.ERROR)\n",
        "train_url = 'https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/train.tfrecord'\n",
        "train_path = tf.keras.utils.get_file(train_url.split('/')[-1], train_url)\n",
        "test_url = 'https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/test.tfrecord'\n",
        "test_path = tf.keras.utils.get_file(test_url.split('/')[-1], test_url)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "6W7aZ9qspZVj",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Construction d'un mod\u00e8le d'analyse des sentiments"
      ]
    },
    {
      "metadata": {
        "id": "jieA0k_NLS8a",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Vous allez effectuer, sur ces donn\u00e9es, l'entra\u00eenement d'un mod\u00e8le d'analyse des sentiments qui pr\u00e9dit si une critique est globalement *favorable* (libell\u00e9\u00a01) ou *d\u00e9favorable* (libell\u00e9\u00a00).\n",
        "\n",
        "Pour ce faire, vous allez transformer la valeur de cha\u00eene `terms` en vecteurs de caract\u00e9ristiques en utilisant un *vocabulaire*, c'est-\u00e0-dire une liste de termes que vous vous attendez \u00e0 trouver dans nos donn\u00e9es. Pour les besoins de cet exercice, nous avons cr\u00e9\u00e9 un vocabulaire r\u00e9duit qui porte sur un ensemble limit\u00e9 de termes. Il s'est av\u00e9r\u00e9 que la plupart de ces termes indiquaient clairement un fort sentiment *favorable* ou *d\u00e9favorable*. D'autres cependant ont simplement \u00e9t\u00e9 ajout\u00e9s, car ils \u00e9taient int\u00e9ressants.\n",
        "\n",
        "Chaque terme du vocabulaire est associ\u00e9 \u00e0 une coordonn\u00e9e du vecteur de caract\u00e9ristiques. Pour convertir la valeur de cha\u00eene `terms` d'un exemple dans ce format de vecteur, vous allez coder comme suit\u00a0: chaque coordonn\u00e9e re\u00e7oit la valeur\u00a00 si le terme de vocabulaire ne figure pas dans l'exemple de cha\u00eene, et la valeur\u00a01 dans le cas contraire. Les termes qui ne figurent pas dans le vocabulaire sont rejet\u00e9s."
      ]
    },
    {
      "metadata": {
        "id": "2HSfklfnLS8b",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " **REMARQUE\u00a0:** *Vous pourriez bien s\u00fbr utiliser un vocabulaire plus vaste\u00a0; il existe de nombreux outils sp\u00e9cialis\u00e9s pour cela. Au lieu de simplement ignorer les termes qui ne figurent pas dans le vocabulaire, vous pourriez \u00e9galement introduire un petit nombre de segments OOV pour hacher les termes hors\u00a0vocabulaire. Vous pourriez aussi utiliser une m\u00e9thode ax\u00e9e sur le __hachage de caract\u00e9ristiques__ qui hache chaque terme, au lieu de cr\u00e9er un vocabulaire explicite. Cela fonctionne tr\u00e8s bien dans la pratique, mais au d\u00e9triment de l'interpr\u00e9tabilit\u00e9, qui s'av\u00e8re utile pour cet exercice. Pour consulter les outils adapt\u00e9s \u00e0 ce type d'op\u00e9ration, reportez-vous au module tf.feature_column.*"
      ]
    },
    {
      "metadata": {
        "id": "Uvoa2HyDtgqe",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Construction du pipeline d'entr\u00e9e"
      ]
    },
    {
      "metadata": {
        "id": "O20vMEOurDol",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Commencez par cr\u00e9er le pipeline d'entr\u00e9e pour importer les donn\u00e9es dans un mod\u00e8le TensorFlow. Vous pouvez utiliser la fonction suivante pour analyser les donn\u00e9es de test et d'apprentissage (qui se trouvent au format [TFRecord](https://www.tensorflow.org/guide/datasets#consuming_tfrecord_data)), et renvoyer un dictionnaire des caract\u00e9ristiques et les libell\u00e9s correspondants."
      ]
    },
    {
      "metadata": {
        "id": "SxxNIEniPq2z",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def _parse_function(record):\n",
        "  \"\"\"Extracts features and labels.\n",
        "  \n",
        "  Args:\n",
        "    record: File path to a TFRecord file    \n",
        "  Returns:\n",
        "    A `tuple` `(labels, features)`:\n",
        "      features: A dict of tensors representing the features\n",
        "      labels: A tensor with the corresponding labels.\n",
        "  \"\"\"\n",
        "  features = {\n",
        "    \"terms\": tf.VarLenFeature(dtype=tf.string), # terms are strings of varying lengths\n",
        "    \"labels\": tf.FixedLenFeature(shape=[1], dtype=tf.float32) # labels are 0 or 1\n",
        "  }\n",
        "  \n",
        "  parsed_features = tf.parse_single_example(record, features)\n",
        "  \n",
        "  terms = parsed_features['terms'].values\n",
        "  labels = parsed_features['labels']\n",
        "\n",
        "  return  {'terms':terms}, labels"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "SXhTeeYMrp-l",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Pour v\u00e9rifier que cela fonctionne comme pr\u00e9vu, construisez un ensemble `TFRecordDataset` pour les donn\u00e9es d'apprentissage, et associez ces derni\u00e8res aux caract\u00e9ristiques et aux libell\u00e9s \u00e0 l'aide de la fonction ci-dessus."
      ]
    },
    {
      "metadata": {
        "id": "oF4YWXR0Omt0",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Create the Dataset object.\n",
        "ds = tf.data.TFRecordDataset(train_path)\n",
        "# Map features and labels with the parse function.\n",
        "ds = ds.map(_parse_function)\n",
        "\n",
        "ds"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "bUoMvK-9tVXP",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Ex\u00e9cutez la cellule suivante pour r\u00e9cup\u00e9rer le premier exemple de l'ensemble de donn\u00e9es d'apprentissage."
      ]
    },
    {
      "metadata": {
        "id": "Z6QE2DWRUc4E",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "n = ds.make_one_shot_iterator().get_next()\n",
        "sess = tf.Session()\n",
        "sess.run(n)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "jBU39UeFty9S",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Vous allez maintenant construire une fonction d'entr\u00e9e formelle que vous pouvez transmettre \u00e0 la m\u00e9thode `train()` d'un objet TensorFlow\u00a0Estimator."
      ]
    },
    {
      "metadata": {
        "id": "5_C5-ueNYIn_",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Create an input_fn that parses the tf.Examples from the given files,\n",
        "# and split them into features and targets.\n",
        "def _input_fn(input_filenames, num_epochs=None, shuffle=True):\n",
        "  \n",
        "  # Same code as above; create a dataset and map features and labels.\n",
        "  ds = tf.data.TFRecordDataset(input_filenames)\n",
        "  ds = ds.map(_parse_function)\n",
        "\n",
        "  if shuffle:\n",
        "    ds = ds.shuffle(10000)\n",
        "\n",
        "  # Our feature data is variable-length, so we pad and batch\n",
        "  # each field of the dataset structure to whatever size is necessary.     \n",
        "  ds = ds.padded_batch(25, ds.output_shapes)\n",
        "  \n",
        "  ds = ds.repeat(num_epochs)\n",
        "\n",
        "  \n",
        "  # Return the next batch of data.\n",
        "  features, labels = ds.make_one_shot_iterator().get_next()\n",
        "  return features, labels"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "Y170tVlrLS8c",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a01\u00a0: Utiliser un mod\u00e8le lin\u00e9aire avec des entr\u00e9es clairsem\u00e9es et un vocabulaire explicite\n",
        "\n",
        "Pour le premier mod\u00e8le, vous allez construire un mod\u00e8le [`LinearClassifier`](https://www.tensorflow.org/api_docs/python/tf/estimator/LinearClassifier) \u00e0 l'aide de 50\u00a0termes informatifs\u00a0; commencez toujours simplement\u00a0!\n",
        "\n",
        "Le code suivant construit la colonne de caract\u00e9ristiques pour ces termes. La fonction [`categorical_column_with_vocabulary_list`](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_list) cr\u00e9e une colonne de caract\u00e9ristiques avec la mise en correspondance cha\u00eene/vecteur de caract\u00e9ristiques."
      ]
    },
    {
      "metadata": {
        "id": "B5gdxuWsvPcx",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# 50 informative terms that compose our model vocabulary. \n",
        "informative_terms = (\"bad\", \"great\", \"best\", \"worst\", \"fun\", \"beautiful\",\n",
        "                     \"excellent\", \"poor\", \"boring\", \"awful\", \"terrible\",\n",
        "                     \"definitely\", \"perfect\", \"liked\", \"worse\", \"waste\",\n",
        "                     \"entertaining\", \"loved\", \"unfortunately\", \"amazing\",\n",
        "                     \"enjoyed\", \"favorite\", \"horrible\", \"brilliant\", \"highly\",\n",
        "                     \"simple\", \"annoying\", \"today\", \"hilarious\", \"enjoyable\",\n",
        "                     \"dull\", \"fantastic\", \"poorly\", \"fails\", \"disappointing\",\n",
        "                     \"disappointment\", \"not\", \"him\", \"her\", \"good\", \"time\",\n",
        "                     \"?\", \".\", \"!\", \"movie\", \"film\", \"action\", \"comedy\",\n",
        "                     \"drama\", \"family\")\n",
        "\n",
        "terms_feature_column = tf.feature_column.categorical_column_with_vocabulary_list(key=\"terms\", vocabulary_list=informative_terms)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "eTiDwyorwd3P",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Vous allez ensuite construire le mod\u00e8le `LinearClassifier`, l'entra\u00eener sur l'ensemble d'apprentissage et l'\u00e9valuer sur l'ensemble d'\u00e9valuation. Apr\u00e8s avoir parcouru le code, ex\u00e9cutez-le et voyons comment vous vous en sortez."
      ]
    },
    {
      "metadata": {
        "id": "HYKKpGLqLS8d",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "my_optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)\n",
        "my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
        "\n",
        "feature_columns = [ terms_feature_column ]\n",
        "\n",
        "\n",
        "classifier = tf.estimator.LinearClassifier(\n",
        "  feature_columns=feature_columns,\n",
        "  optimizer=my_optimizer,\n",
        ")\n",
        "\n",
        "classifier.train(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "print(\"Training set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([test_path]),\n",
        "  steps=1000)\n",
        "\n",
        "print(\"Test set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "J0ubn9gULS8g",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a02\u00a0: Utiliser un mod\u00e8le de r\u00e9seau neuronal profond (DNN)\n",
        "\n",
        "Le mod\u00e8le ci-dessus est de type lin\u00e9aire. Il fonctionne relativement bien. Cependant, peut-on obtenir de meilleurs r\u00e9sultats avec un mod\u00e8le DNN\u00a0?\n",
        "\n",
        "Rempla\u00e7ons le mod\u00e8le `LinearClassifier` par le mod\u00e8le [`DNNClassifier`](https://www.tensorflow.org/api_docs/python/tf/estimator/DNNClassifier). Ex\u00e9cutez la cellule suivante et voyons comment vous vous en sortez."
      ]
    },
    {
      "metadata": {
        "id": "jcgOPfEALS8h",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "##################### Here's what we changed ##################################\n",
        "classifier = tf.estimator.DNNClassifier(                                      #\n",
        "  feature_columns=[tf.feature_column.indicator_column(terms_feature_column)], #\n",
        "  hidden_units=[20,20],                                                       #\n",
        "  optimizer=my_optimizer,                                                     #\n",
        ")                                                                             #\n",
        "###############################################################################\n",
        "\n",
        "try:\n",
        "  classifier.train(\n",
        "    input_fn=lambda: _input_fn([train_path]),\n",
        "    steps=1000)\n",
        "\n",
        "  evaluation_metrics = classifier.evaluate(\n",
        "    input_fn=lambda: _input_fn([train_path]),\n",
        "    steps=1)\n",
        "  print(\"Training set metrics:\")\n",
        "  for m in evaluation_metrics:\n",
        "    print(m, evaluation_metrics[m])\n",
        "  print(\"---\")\n",
        "\n",
        "  evaluation_metrics = classifier.evaluate(\n",
        "    input_fn=lambda: _input_fn([test_path]),\n",
        "    steps=1)\n",
        "\n",
        "  print(\"Test set metrics:\")\n",
        "  for m in evaluation_metrics:\n",
        "    print(m, evaluation_metrics[m])\n",
        "  print(\"---\")\n",
        "except ValueError as err:\n",
        "  print(err)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "cZz68luxLS8j",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a03\u00a0: Utiliser une repr\u00e9sentation vectorielle continue avec un mod\u00e8le DNN\n",
        "\n",
        "Dans le cadre de cette t\u00e2che, vous allez mettre en \u0153uvre un mod\u00e8le DNN \u00e0 l'aide d'une colonne de repr\u00e9sentations vectorielles continues. Une colonne de ce type accepte des donn\u00e9es rares en guise d'entr\u00e9e et renvoie un vecteur dense de plus faible dimension en sortie."
      ]
    },
    {
      "metadata": {
        "id": "AliRzhvJLS8k",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " **REMARQUE\u00a0:** *En r\u00e8gle g\u00e9n\u00e9rale, une colonne de repr\u00e9sentations vectorielles continues constitue l'option la plus efficace du point de vue de l'utilisation des ressources pour entra\u00eener un mod\u00e8le sur des donn\u00e9es rares. Dans une [section facultative](#scrollTo=XDMlGgRfKSVz) \u00e0 la fin de cet exercice, nous examinerons plus en d\u00e9tail les diff\u00e9rences de mise en \u0153uvre entre l'utilisation des options `embedding_column` et `indicator_column`, ainsi que les avantages et inconv\u00e9nients de ces deux options.*"
      ]
    },
    {
      "metadata": {
        "id": "F-as3PtALS8l",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Effectuez ce qui suit dans le code ci-dessous\u00a0:\n",
        "\n",
        "* D\u00e9finissez les colonnes de caract\u00e9ristiques pour le mod\u00e8le \u00e0 l'aide d'une option `embedding_column` qui projette les donn\u00e9es dans deux dimensions (voir les [documents TF](https://www.tensorflow.org/api_docs/python/tf/feature_column/embedding_column) pour en savoir plus sur la signature de fonction pour `embedding_column`).\n",
        "* D\u00e9finissez un mod\u00e8le `DNNClassifier` avec les sp\u00e9cifications suivantes\u00a0:\n",
        "  * Deux couches cach\u00e9es de 20\u00a0unit\u00e9s chacune\n",
        "  * Une optimisation AdaGrad avec un taux d'apprentissage de 0,1\n",
        "  * Une valeur `gradient_clip_norm` de 5,0"
      ]
    },
    {
      "metadata": {
        "id": "UlPZ-Q9bLS8m",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " **REMARQUE\u00a0:** *Dans la pratique, vous pourriez projeter des donn\u00e9es dans un nombre de dimensions sup\u00e9rieur \u00e0 2\u00a0; 50 ou 100, par exemple. Cependant, pour l'instant, se limiter \u00e0 deux\u00a0dimensions permet une visualisation ais\u00e9e.*"
      ]
    },
    {
      "metadata": {
        "id": "mNCLhxsXyOIS",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Astuce"
      ]
    },
    {
      "metadata": {
        "id": "L67xYD7hLS8m",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Here's a example code snippet you might use to define the feature columns:\n",
        "\n",
        "terms_embedding_column = tf.feature_column.embedding_column(terms_feature_column, dimension=2)\n",
        "feature_columns = [ terms_embedding_column ]"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "iv1UBsJxyV37",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Ex\u00e9cutez le code ci-dessous"
      ]
    },
    {
      "metadata": {
        "id": "5PG_yhNGLS8u",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "########################## YOUR CODE HERE ######################################\n",
        "terms_embedding_column = # Define the embedding column\n",
        "feature_columns = # Define the feature columns\n",
        "\n",
        "classifier = # Define the DNNClassifier\n",
        "################################################################################\n",
        "\n",
        "classifier.train(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "print(\"Training set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([test_path]),\n",
        "  steps=1000)\n",
        "\n",
        "print(\"Test set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "eQS5KQzBybTY",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Solution\n",
        "\n",
        "Cliquez ci-dessous pour afficher la solution."
      ]
    },
    {
      "metadata": {
        "id": "R5xOdYeQydi5",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "########################## SOLUTION CODE ########################################\n",
        "terms_embedding_column = tf.feature_column.embedding_column(terms_feature_column, dimension=2)\n",
        "feature_columns = [ terms_embedding_column ]\n",
        "\n",
        "my_optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)\n",
        "my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
        "\n",
        "classifier = tf.estimator.DNNClassifier(\n",
        "  feature_columns=feature_columns,\n",
        "  hidden_units=[20,20],\n",
        "  optimizer=my_optimizer\n",
        ")\n",
        "#################################################################################\n",
        "\n",
        "classifier.train(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "print(\"Training set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([test_path]),\n",
        "  steps=1000)\n",
        "\n",
        "print(\"Test set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "aiHnnVtzLS8w",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a04\u00a0: Se convaincre qu'il existe r\u00e9ellement une repr\u00e9sentation vectorielle continue\n",
        "\n",
        "L'option `embedding_column` est utilis\u00e9e dans le mod\u00e8le ci-dessus et cela semble fonctionner. Cependant, cela ne nous donne pas beaucoup d'informations sur ce qui se passe en interne. Dans ce cas, comment v\u00e9rifier que le mod\u00e8le utilise effectivement une repr\u00e9sentation vectorielle continue en interne\u00a0?\n",
        "\n",
        "Pour commencer, examinez les Tensors qui se trouvent dans le mod\u00e8le\u00a0:"
      ]
    },
    {
      "metadata": {
        "id": "h1jNgLdQLS8w",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "classifier.get_variable_names()"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "Sl4-VctMLS8z",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Comme vous pouvez le voir, le mod\u00e8le contient une couche de repr\u00e9sentations vectorielles continues\u00a0: `'dnn/input_from_feature_columns/input_layer/terms_embedding/...'`. (Ce qui est int\u00e9ressant ici, c'est que cette couche peut \u00eatre apprise avec le reste du mod\u00e8le, comme c'est le cas pour n'importe quelle couche cach\u00e9e.)\n",
        "\n",
        "La forme de la couche de repr\u00e9sentations vectorielles continues est-elle correcte\u00a0? Pour le savoir, ex\u00e9cutez le code suivant."
      ]
    },
    {
      "metadata": {
        "id": "JNFxyQUiLS80",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " **REMARQUE\u00a0:** *Dans le cas pr\u00e9sent, souvenez-vous que la repr\u00e9sentation vectorielle continue est une matrice qui nous permet de projeter un vecteur allant de 50\u00a0dimensions jusqu'\u00e0 2\u00a0dimensions.*"
      ]
    },
    {
      "metadata": {
        "id": "1xMbpcEjLS80",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "classifier.get_variable_value('dnn/input_from_feature_columns/input_layer/terms_embedding/embedding_weights').shape"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "MnLCIogjLS82",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Consacrez du temps \u00e0 la v\u00e9rification manuelle des diff\u00e9rentes couches et formes pour vous assurer que tout est correctement connect\u00e9."
      ]
    },
    {
      "metadata": {
        "id": "rkKAaRWDLS83",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a05\u00a0: Examiner la repr\u00e9sentation vectorielle continue\n",
        "\n",
        "Nous allons \u00e0 pr\u00e9sent nous pencher sur l'espace de repr\u00e9sentation vectorielle proprement dit et voir o\u00f9 se classent les termes. Pour cela, proc\u00e9dez comme suit\u00a0:\n",
        "1. Ex\u00e9cutez le code suivant pour voir la repr\u00e9sentation vectorielle continue dont vous avez effectu\u00e9 l'apprentissage \u00e0 la **T\u00e2che\u00a03**. Est-ce que tout est conforme \u00e0 vos attentes\u00a0?\n",
        "\n",
        "2. Entra\u00eenez \u00e0 nouveau le mod\u00e8le en ex\u00e9cutant une nouvelle fois le code de la **T\u00e2che\u00a03**, puis ex\u00e9cutez \u00e0 nouveau la visualisation des repr\u00e9sentations vectorielles continues ci-dessous. Quels sont les changements\u00a0? Quels sont les \u00e9l\u00e9ments qui restent identiques\u00a0?\n",
        "\n",
        "3. Pour terminer, entra\u00eenez \u00e0 nouveau le mod\u00e8le en utilisant seulement 10\u00a0mesures (ce qui donnera lieu \u00e0 un tr\u00e8s mauvais mod\u00e8le). Ex\u00e9cutez \u00e0 nouveau la repr\u00e9sentation vectorielle continue ci-dessous. Que voyez-vous maintenant, et pourquoi\u00a0?"
      ]
    },
    {
      "metadata": {
        "id": "s4NNu7KqLS84",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "embedding_matrix = classifier.get_variable_value('dnn/input_from_feature_columns/input_layer/terms_embedding/embedding_weights')\n",
        "\n",
        "for term_index in range(len(informative_terms)):\n",
        "  # Create a one-hot encoding for our term.  It has 0s everywhere, except for\n",
        "  # a single 1 in the coordinate that corresponds to that term.\n",
        "  term_vector = np.zeros(len(informative_terms))\n",
        "  term_vector[term_index] = 1\n",
        "  # We'll now project that one-hot vector into the embedding space.\n",
        "  embedding_xy = np.matmul(term_vector, embedding_matrix)\n",
        "  plt.text(embedding_xy[0],\n",
        "           embedding_xy[1],\n",
        "           informative_terms[term_index])\n",
        "\n",
        "# Do a little setup to make sure the plot displays nicely.\n",
        "plt.rcParams[\"figure.figsize\"] = (15, 15)\n",
        "plt.xlim(1.2 * embedding_matrix.min(), 1.2 * embedding_matrix.max())\n",
        "plt.ylim(1.2 * embedding_matrix.min(), 1.2 * embedding_matrix.max())\n",
        "plt.show() "
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "pUb3L7pqLS86",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a06\u00a0: Essayer d'am\u00e9liorer les performances du mod\u00e8le\n",
        "\n",
        "Voyez s'il est possible d'affiner le mod\u00e8le afin d'en am\u00e9liorer les performances. Voici quelques pistes\u00a0:\n",
        "\n",
        "* **Modifier les hyperparam\u00e8tres** ou **utiliser un autre optimiseur** comme Adam (ces strat\u00e9gies vous permettront de gagner, tout au plus, un ou deux points de pourcentage de pr\u00e9cision).\n",
        "* **Ajouter des termes aux `informative_terms`.** Vous trouverez, \u00e0 l'adresse suivante, un fichier de vocabulaire complet contenant 30\u00a0716\u00a0termes pour cet ensemble de donn\u00e9es\u00a0: https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/terms.txt. Vous pouvez choisir des termes suppl\u00e9mentaires dans ce fichier ou l'utiliser int\u00e9gralement via la colonne de caract\u00e9ristiques `categorical_column_with_vocabulary_file`."
      ]
    },
    {
      "metadata": {
        "id": "6-b3BqXvLS86",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Download the vocabulary file.\n",
        "terms_url = 'https://download.mlcc.google.com/mledu-datasets/sparse-data-embedding/terms.txt'\n",
        "terms_path = tf.keras.utils.get_file(terms_url.split('/')[-1], terms_url)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "0jbJlwW5LS8-",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Create a feature column from \"terms\", using a full vocabulary file.\n",
        "informative_terms = None\n",
        "with io.open(terms_path, 'r', encoding='utf8') as f:\n",
        "  # Convert it to a set first to remove duplicates.\n",
        "  informative_terms = list(set(f.read().split()))\n",
        "  \n",
        "terms_feature_column = tf.feature_column.categorical_column_with_vocabulary_list(key=\"terms\", \n",
        "                                                                                 vocabulary_list=informative_terms)\n",
        "\n",
        "terms_embedding_column = tf.feature_column.embedding_column(terms_feature_column, dimension=2)\n",
        "feature_columns = [ terms_embedding_column ]\n",
        "\n",
        "my_optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)\n",
        "my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
        "\n",
        "classifier = tf.estimator.DNNClassifier(\n",
        "  feature_columns=feature_columns,\n",
        "  hidden_units=[10,10],\n",
        "  optimizer=my_optimizer\n",
        ")\n",
        "\n",
        "classifier.train(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([train_path]),\n",
        "  steps=1000)\n",
        "print(\"Training set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")\n",
        "\n",
        "evaluation_metrics = classifier.evaluate(\n",
        "  input_fn=lambda: _input_fn([test_path]),\n",
        "  steps=1000)\n",
        "\n",
        "print(\"Test set metrics:\")\n",
        "for m in evaluation_metrics:\n",
        "  print(m, evaluation_metrics[m])\n",
        "print(\"---\")"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ew3kwGM-LS9B",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Le mot de la fin\n",
        "\n",
        "La solution DNN avec repr\u00e9sentation vectorielle continue que nous avons d\u00e9velopp\u00e9e est peut-\u00eatre meilleure que notre mod\u00e8le lin\u00e9aire d'origine. Cependant, ce dernier offrait \u00e9galement de bons r\u00e9sultats et son apprentissage s'av\u00e9rait bien plus rapide. Cette vitesse d'apprentissage est due au fait que les mod\u00e8les lin\u00e9aires ne comptent pas autant de param\u00e8tres \u00e0 mettre \u00e0 jour, ni autant de couches \u00e0 traverser pour la r\u00e9tropropagation.\n",
        "\n",
        "Pour certaines applications, la vitesse des mod\u00e8les lin\u00e9aires peut changer la donne ou, d'un point de vue qualitatif, ces mod\u00e8les peuvent s'av\u00e9rer bien suffisants. Dans d'autres cas, la complexit\u00e9 accrue du mod\u00e8le et la capacit\u00e9 fournie par les solutions DNN peuvent \u00eatre plus importantes. Lorsque vous d\u00e9finissez l'architecture de votre mod\u00e8le, \u00e9tudiez le probl\u00e8me de mani\u00e8re suffisamment approfondie pour d\u00e9terminer l'espace dans lequel vous vous trouvez."
      ]
    },
    {
      "metadata": {
        "id": "9MquXy9zLS9B",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### *Discussion facultative\u00a0:* Avantages et inconv\u00e9nients de l'option `embedding_column` par rapport \u00e0 `indicator_column`\n",
        "\n",
        "D'un point de vue conceptuel, un adaptateur est n\u00e9cessaire pour utiliser une colonne de donn\u00e9es rares lors de l'apprentissage d'un mod\u00e8le `LinearClassifier` ou `DNNClassifier`. TF propose deux options\u00a0: `embedding_column` et `indicator_column`.\n",
        "\n",
        "Lors de l'apprentissage d'un mod\u00e8le LinearClassifier (comme dans la **T\u00e2che\u00a01**), l'option `embedding_column` est utilis\u00e9e. Comme nous l'avons vu dans la **T\u00e2che\u00a02**, lors de l'apprentissage d'un mod\u00e8le `DNNClassifier`, vous devez choisir explicitement `embedding_column` ou `indicator_column`. Cette section d\u00e9crit la distinction entre ces deux options, ainsi que les avantages et inconv\u00e9nients de l'une par rapport \u00e0 l'autre, \u00e0 l'aide d'un exemple simple."
      ]
    },
    {
      "metadata": {
        "id": "M_3XuZ_LLS9C",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Prenons comme exemple des donn\u00e9es rares contenant les valeurs `\"great\"`, `\"beautiful\"` et `\"excellent\"`. \u00c9tant donn\u00e9 que la taille du vocabulaire utilis\u00e9 ici est de $V = 50$, chaque unit\u00e9 (neurone) de la premi\u00e8re couche aura 50\u00a0pond\u00e9rations. Pour repr\u00e9senter le nombre de termes d'une entr\u00e9e clairsem\u00e9e, nous allons utiliser $s$. Pour cet exemple de donn\u00e9es rares, nous avons donc $s\u00a0=\u00a03$. Pour une couche d'entr\u00e9e avec $V$\u00a0valeurs possibles, une couche cach\u00e9e avec $d$\u00a0unit\u00e9s doit effectuer la multiplication d'un vecteur par une matrice\u00a0: $(1 \\times V) * (V \\times d)$. Le co\u00fbt de calcul de cette op\u00e9ration est de $O(V * d)$. Notez que ce co\u00fbt est proportionnel au nombre de pond\u00e9rations dans cette couche cach\u00e9e et ind\u00e9pendant de $s$.\n",
        "\n",
        "Si les entr\u00e9es sont encod\u00e9es au format one-hot (un vecteur bool\u00e9en de longueur $V$ avec la valeur\u00a01 pour les termes pr\u00e9sents et la valeur\u00a00 pour le reste) \u00e0 l'aide de l'option [`indicator_column`](https://www.tensorflow.org/api_docs/python/tf/feature_column/indicator_column), cela signifie qu'une multiplication est n\u00e9cessaire et qu'il faut ajouter beaucoup de z\u00e9ros."
      ]
    },
    {
      "metadata": {
        "id": "I7mR4Wa2LS9C",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Lorsque l'on obtient exactement les m\u00eames r\u00e9sultats en utilisant une option [`embedding_column`](https://www.tensorflow.org/api_docs/python/tf/feature_column/embedding_column) de taille $d$, on recherche et on ajoute simplement les repr\u00e9sentations vectorielles continues correspondant aux trois caract\u00e9ristiques pr\u00e9sentes dans notre exemple d'entr\u00e9e de `\"great\"`, `\"beautiful\"`, `\"excellent\"`: $(1 \\times d) + (1 \\times d) + (1 \\times d)$. \u00c9tant donn\u00e9 que les pond\u00e9rations relatives aux caract\u00e9ristiques qui sont absentes sont multipli\u00e9es par\u00a00 dans la multiplication d'un vecteur par une matrice, elles n'entrent pas en ligne de compte dans le r\u00e9sultat. Les pond\u00e9rations relatives aux caract\u00e9ristiques pr\u00e9sentes sont, elles, multipli\u00e9es par\u00a01. D\u00e8s lors, l'ajout des pond\u00e9rations obtenues par le biais de la recherche des repr\u00e9sentations vectorielles continues donnera le m\u00eame r\u00e9sultat que la multiplication d'un vecteur par une matrice.\n",
        "\n",
        "Lorsque vous utilisez des repr\u00e9sentations vectorielles continues, calculer la recherche de ces repr\u00e9sentations correspond \u00e0 $O(s * d)$, ce qui, sur le plan de l'utilisation des ressources, s'av\u00e8re beaucoup plus efficace que le co\u00fbt $O(V * d)$ de l'option `indicator_column` dans les donn\u00e9es rares, pour laquelle $s$ est sensiblement plus petit que $V$. (Pour rappel, ces repr\u00e9sentations vectorielles continues sont en cours d'apprentissage. Dans n'importe quelle it\u00e9ration d'apprentissage, ce sont les pond\u00e9rations en cours qui sont recherch\u00e9es.)"
      ]
    },
    {
      "metadata": {
        "id": "etZ9qf0kLS9D",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Comme nous l'avons vu dans la **T\u00e2che\u00a03**, lorsque l'on utilise une option `embedding_column` pour effectuer l'apprentissage du mod\u00e8le `DNNClassifier`, notre mod\u00e8le apprend une repr\u00e9sentation bidimensionnelle des caract\u00e9ristiques, dans laquelle le produit scalaire d\u00e9finit une statistique de similitude adapt\u00e9e \u00e0 la t\u00e2che souhait\u00e9e. Dans cet exemple, les termes utilis\u00e9s de la m\u00eame fa\u00e7on dans le cadre des critiques de films (`\"great\"` et `\"excellent\"`, par exemple) seront plus proches les uns des autres dans l'espace de repr\u00e9sentation vectorielle (grand produit scalaire), tandis que les termes dissemblables (`\"great\"` et `\"bad\"`, par exemple) seront plus \u00e9loign\u00e9s (petit produit scalaire)."
      ]
    }
  ]
}
